package cn.lingyangwl.agile.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.lingyangwl.agile.common.core.exception.enums.*;
import cn.lingyangwl.agile.infra.*;
import cn.lingyangwl.agile.infra.module.datamanage.db.*;
import cn.lingyangwl.agile.infra.module.group.model.rqrs.*;
import cn.lingyangwl.agile.model.enums.*;
import cn.lingyangwl.agile.system.assembly.*;
import cn.lingyangwl.agile.system.mapper.*;
import cn.lingyangwl.agile.system.model.entity.*;
import cn.lingyangwl.agile.system.model.rqrs.diy.*;
import cn.lingyangwl.agile.system.service.*;
import cn.lingyangwl.framework.mybatis.plus.utils.*;
import cn.lingyangwl.framework.tool.core.*;
import cn.lingyangwl.framework.tool.core.exception.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yitter.idgen.YitIdHelper;
import com.google.common.base.CaseFormat;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 表单
 *
 * @author shenguangyang
 */
@Slf4j
@Service
public class DiyFormServiceImpl extends ServiceImpl<DiyFormMapper, DiyForm>
    implements IService<DiyForm>, DiyFormService {
    @Resource
    private DiyFormAssembly diyFormAssembly;
    @Resource
    private InfraApi infraApi;
    @Resource
    private DiyFormFieldService formFieldService;
    
    @Override
    public IPage<DiyFormResp> pageDiyForm(DiyFormQueryReq req) {
        LocalDateTime beginTime = req.getBeginLocalDateTime();
        LocalDateTime endTime = req.getEndLocalDateTime();
        LambdaQueryWrapper<DiyForm> lqw = DiyForm.lqw()
            .eq(StringUtils.isNotEmpty(req.getStatus()), DiyForm::getStatus, req.getStatus())
            .eq(Objects.nonNull(req.getGroupId()), DiyForm::getGroupId, req.getGroupId())
            .ge(Objects.nonNull(beginTime), DiyForm::getCreatedAt, beginTime)
            .le(Objects.nonNull(endTime), DiyForm::getCreatedAt, endTime)
            .orderByDesc(DiyForm::getCreatedAt);
        IPage<DiyForm> pageData = this.page(PageUtil.to(req), lqw);
        if (CollUtil.isEmpty(pageData.getRecords())) {
            return new Page<>();
        }
        List<Long> groupIds = pageData.getRecords().stream().map(DiyForm::getGroupId).distinct().collect(Collectors.toList());
        groupIds.add(-1L);
        DataGroupQuery groupReq = new DataGroupQuery();
        groupReq.setIds(groupIds).setBizType(DataGroupBizTypeEnum.DIY_FORM.getType());
        Map<Long, String> groupMap = infraApi.listDataGroup(groupReq).stream()
            .collect(Collectors.toMap(DataGroupDto::getId, DataGroupBase::getName));

        return PageUtil.to(pageData, (e) -> {
            DiyFormResp resp = diyFormAssembly.toDiyFormResp(e);
            resp.setGroupName(groupMap.get(e.getGroupId()));
            return resp;
        });
    }

    @Override
    public DiyFormResp getDiyForm(Long id) {
        DiyForm entity = this.getById(id);
        if (Objects.isNull(entity)) {
            return null;
        }
        DiyFormResp resp = diyFormAssembly.toDiyFormResp(entity);
        DataGroupDto dataGroup = infraApi.getDataGroup(resp.getGroupId());
        resp.setGroupName(Optional.ofNullable(dataGroup).map(DataGroupDto::getName).orElse(""));
    	return resp;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveDiyForm(DiyFormSaveReq req) {
        DiyForm entity = diyFormAssembly.toDiyForm(req);
        boolean ret = this.save(entity);
        this.formFieldService.saveFields(entity, req.getFieldList());
        return ret;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateDiyForm(DiyFormSaveReq req) {
        this.formFieldService.saveOrUpdateBatch(req.getFieldList());
        DiyForm entity = diyFormAssembly.toDiyForm(req);
        this.formFieldService.saveFields(entity, req.getFieldList());
    	return this.updateById(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteDiyForm(Long id) {
        formFieldService.deleteField(Collections.singletonList(id), null);
    	return this.removeById(id);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchDeleteDiyForm(List<Long> ids) {
        formFieldService.deleteField(ids, null);
    	return this.removeByIds(ids);
    }

    @Override
    public boolean deleteGroup(DataGroupDeleteReq req) {
        DataGroupQuery listReq = new DataGroupQuery()
            .setIds(req.getIds()).setBizType(req.getBizType());
        List<Long> groupIds = infraApi.listDataGroup(listReq).stream()
            .map(DataGroupDto::getId).collect(Collectors.toList());
        if (CollUtil.isEmpty(groupIds)) {
            return true;
        }
        LambdaQueryWrapper<DiyForm> lqw = DiyForm.lqw().in(DiyForm::getGroupId, groupIds);
        if (this.count(lqw) > 0) {
            throw new BizException(GlobalErrorEnum.DATA_GROUP_USED);
        }

        DataGroupDeleteReq deleteReq = new DataGroupDeleteReq();
        deleteReq.setBizType(DataGroupBizTypeEnum.DIY_FORM.getType()).setIds(req.getIds());
        infraApi.batchDeleteDataGroup(deleteReq);
        return infraApi.batchDeleteDataGroup(deleteReq);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importTable(DiyFormImportTableReq req) {
        List<SQLContext> contextList = infraApi.listSqlContext(req.getDatasourceId(), req.getTableNames());

        // 查询已有的数据集数据
        LambdaQueryWrapper<DiyForm> lqw = DiyForm.lqw().eq(DiyForm::getDatasourceId, req.getDatasourceId()).eq(DiyForm::getGroupId, req.getGroupId());
        List<DiyForm> formList = this.list(lqw);

        List<Long> formIds = formList.stream().map(DiyForm::getId).collect(Collectors.toList());
        Map<String, DiyForm> formMap = formList.stream()
            .collect(Collectors.toMap(e -> e.getDatasourceId() + e.getTableName(), Function.identity()));

        // 查询已有的数据集的列数据
        List<DiyFormField> fieldList = formFieldService.listField(formIds);
        Map<Long, List<DiyFormField>> fieldMap = fieldList.stream().collect(Collectors.groupingBy(DiyFormField::getFormId));

        List<DiyFormField> saveColumnList = new ArrayList<>();
        for (SQLContext sqlContext : contextList) {
            TableDefinition tableDefinition = sqlContext.getTableDefinition();
            // 初始化数据集数据
            String key = req.getDatasourceId() + tableDefinition.getTableName();
            DiyForm diyForm = formMap.getOrDefault(key, new DiyForm());
            boolean isNew = diyForm.getId() == null;
            diyForm.setId(YitIdHelper.nextId());
            diyForm.setTableName(tableDefinition.getTableName())
                .setName(StringUtils.firstNonEmpty(diyForm.getName(), diyForm.getTableName()))
                .setDatasourceId(req.getDatasourceId()).setGroupId(req.getGroupId())
                .setRemark(tableDefinition.getComment());

            Map<String, DiyFormField> formFieldMap = fieldMap.getOrDefault(diyForm.getId(), new ArrayList<>()).stream()
                .collect(Collectors.toMap(DiyFormField::getColumnName, Function.identity()));

            for (ColumnDefinition item : tableDefinition.getColumnDefinitions()) {
                DiyFormField column = formFieldMap.getOrDefault(item.getColumnName(), new DiyFormField());
                // 初始化数据集列数据
                String attrName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, item.getColumnName());
                column.setColumnLength(item.getLength()).setColumnScale(item.getScale())
                    .setColumnName(item.getColumnName())
                    .setAttrName(attrName)
                    .setColumnComment(item.getComment()).setIsNullable(item.getIsNullable())
                    .setSortNo(item.getSortNo()).setIsPk(item.getIsPk()).setFormId(diyForm.getId());
                saveColumnList.add(column);
            }
            if (isNew) {
                this.save(diyForm);
            } else {
                this.updateById(diyForm);
            }
            formFieldService.saveOrUpdateBatch(saveColumnList);
        }
    }
}
